﻿#pragma once

#include "../repo/src/include/root/rpc_root.h"
#include "../util/box_std_allocator.hh"
#include "../util/object_pool.hh"
#include <cstdint>
#include <unordered_map>
#include <unordered_set>

namespace kratos {
namespace service {
class ServiceBox;
}
} // namespace kratos

namespace kratos {
namespace util {
class TimerWheel;
struct WheelNode;
using TimerID = WheelNode *;
constexpr static TimerID INVALID_TIMER_ID = nullptr;
} // namespace util
} // namespace kratos

namespace rpc {
/**
 * ProxyHandler实现.
 */
class ProxyHandlerImpl : public ProxyHandler {
  using GlobalIndexChannelMap = std::unordered_map<
      rpc::GlobalIndex,
      TransportPtr,
      std::hash<GlobalIndex>,
      std::equal_to<GlobalIndex>,
      kratos::service::Allocator<
      std::pair<const rpc::GlobalIndex, TransportPtr>>
  >;
  using ServiceUUIDChannelMap = std::unordered_map<
      rpc::ServiceUUID,
      TransportPtr,
      std::hash<ServiceUUID>,
      std::equal_to<ServiceUUID>,
      kratos::service::Allocator<std::pair<
          const rpc::ServiceUUID, TransportPtr>>
      >;                                                   
  using GlobalIndexerMap = std::unordered_map<
      rpc::GlobalIndex,
      ServiceUUIDChannelMap,
      std::hash<GlobalIndex>,
      std::equal_to<GlobalIndex>,
      kratos::service::Allocator<std::pair<const rpc::GlobalIndex, ServiceUUIDChannelMap>>
  >;
  using GlobalIndexPool = std::unordered_set<
      GlobalIndex,
      std::hash<GlobalIndex>,
      std::equal_to<GlobalIndex>,
      kratos::service::Allocator<rpc::GlobalIndex>
  >;
  /**
   * 一次内部到外部的调用信息.
   */
  struct CallInfo {
    rpc::CallID           callID{INVALID_CALL_ID};                  ///< 服务提供的本次调用ID
    rpc::ServiceUUID      service_uuid{INVALID_SERVICE_UUID};       ///< 本次调用的内部服务UUID
    kratos::util::TimerID timer_id{kratos::util::INVALID_TIMER_ID}; ///< 定时器ID
    TransportPtr          transport;                                ///< 调用返回的管道
  };
  kratos::service::ServiceBox* box_{nullptr};        ///< 服务容器
  std::uint8_t                 seed_{0};             ///< GlobalIndex产生种子
  std::uint32_t                serial_{0};           ///< GlobalIndex自增部分
  std::time_t                  query_timeout_{5000}; ///< 查询远程服务超时，毫秒
  std::time_t                  call_timeout_{5000};  ///< 调用超时，毫秒

  constexpr static GlobalIndex MAX_SERIAL{0xffffff}; ///< GlobalIndex的序号最大值

  GlobalIndexPool           global_index_pool_;          ///< {GlobalIndex}
  GlobalIndexChannelMap     global_index_transport_map_; ///< {GlobalIndex, TransportPtr}, GlobalIndex与外部管道对应表
  GlobalIndexerMap          global_indexer_map_;         ///< {GlobalIndex, ServiceUUIDChannelMap}
  /**
   * 内部到外部调用管理器.
   */
  class InsideCallOutsideManager {
  private:
    using CallIDServiceUUIDMap = std::unordered_map<
        CallID,
        CallInfo,
        std::hash<CallID>,
        std::equal_to<CallID>,
        kratos::service::Allocator<std::pair<const CallID, CallInfo>>
    >;

  CallIDServiceUUIDMap      call_map_;                   ///< {CallID, CallInfo}
  kratos::util::TimerWheel* timer_wheel_{nullptr};       ///< 时间轮

  public:
    /**
     * 构造.
     *
     * \param timer_wheel 时间轮
     */
    InsideCallOutsideManager(kratos::util::TimerWheel *timer_wheel);
    /**
     * 析构.
     *
     */
    ~InsideCallOutsideManager();
    /**
     * 添加一个调用.
     *
     * \param call_timeout 调用超时
     * \param callID 调用ID
     * \param service_uuid 服务UUID
     * \param virtual_call_id 虚拟调用ID
     * \return true或false
     */
    auto add(std::time_t call_timeout, rpc::CallID callID,
             rpc::ServiceUUID service_uuid, rpc::CallID virtual_call_id, TransportPtr& transport)
        -> bool;
    /**
     * 销毁一个调用.
     *
     * \param callID 调用ID
     * \param [OUT]service_uuid 服务UUID
     * \param [OUT]real_call_id 调用ID
     * \return 返回管道
     */
    auto remove(rpc::CallID callID, rpc::ServiceUUID &service_uuid,
                rpc::CallID &real_call_id) -> TransportPtr;
  };

  using GlobalIndexCallInfoMap = std::unordered_map<
      GlobalIndex,
      InsideCallOutsideManager,
      std::hash<GlobalIndex>,
      std::equal_to<GlobalIndex>,
      kratos::service::Allocator<std::pair<const GlobalIndex, InsideCallOutsideManager>>
  >;

  GlobalIndexCallInfoMap inside_to_outside_call_info_map_; ///< 内部到外部的调用信息
  rpc::CallID            proxy_virtual_call_id_{1};        ///< 虚拟调用索引，用来区分不同的调用，防止内部不同服务的调用ID重叠

  kratos::unique_pool_ptr<kratos::util::TimerWheel> timer_wheel_{nullptr}; ///< 定时器时间轮

public:
  /**
   * 构造.
   *
   * \param box 服务容器
   */
  ProxyHandlerImpl(kratos::service::ServiceBox *box);
  /**
   * 析构.
   *
   */
  virtual ~ProxyHandlerImpl();
  // 通过 ProxyHandler 继承
  virtual bool onRelay(TransportPtr &transport,
                       const RpcMsgHeader &header) override;
  /**
   * 主循环.
   *
   * \param now 当前时间戳，毫秒
   */
  void update(std::time_t now);

public:
  /**
   * 设置GlobalIndex种子.
   *
   * \param seed 种子
   * \return
   */
  auto set_seed(std::uint8_t seed) -> void;
  /**
   * 当有非容器连接建立时回调.
   *
   * \param transport 外部管道
   * \return
   */
  auto on_accept(rpc::TransportPtr transport) -> void;
  /**
   * 当有非容器连接断开时回调.
   *
   * \param transport 外部管道
   * \return
   */
  auto on_close(rpc::TransportPtr transport) -> void;

private:
  /**
   * 建立一个新的GlobalIndex.
   *
   * \return GlobalIndex
   */
  auto new_global_index() -> rpc::GlobalIndex;
  /**
   * 通过UUID获取内部容器连接.
   *
   * \param from 外部连接
   * \param uuid 服务UUID
   * \return 内部容器连接
   */
  auto get_service_transport_by_uuid(TransportPtr &from, rpc::ServiceUUID uuid)
      -> TransportPtr;
  /**
   * 尝试获取内部容器连接.
   *
   * \param uuid 服务UUID
   * \param [IN OUT]uuid_channel ServiceUUIDChannelMap
   * \return 内部容器连接，未找到则返回空指针
   */
  auto get_inside_service_transport(rpc::ServiceUUID uuid,
                                    ServiceUUIDChannelMap &uuid_channel)
      -> TransportPtr;
  /**
   * 获取调用返回需要转发的目标内部管道.
   *
   * \param from 外部管道
   * \param header 协议头
   * \param [OUT] real_call_id 内部调用ID
   * \return 内部管道
   */
  auto get_inside_service_transport(TransportPtr &from,
                                    const RpcMsgHeader &header,
                                    rpc::CallID &real_call_id) -> TransportPtr;
  /**
   * 获取外部连接索引.
   *
   * \param from 内部管道
   * \param header 协议头
   * \return 外部连接索引
   */
  auto get_global_index(TransportPtr &from, const RpcMsgHeader &header)
      -> GlobalIndex;
  /**
   * 向外部连接发送一个服务未找到的协议.
   *
   * \param from 外部管道
   * \param callHeader 协议头
   * \return
   */
  auto service_not_found(TransportPtr &from,
                         const rpc::RpcCallHeader &callHeader) -> void;
  /**
   * 记录内部到外部调用的关系.
   *
   * \param global_index 外部连接索引
   * \param transport 内部管道
   * \param [OUT] fake_call_id 虚拟调用ID，用于RPC调用返回后寻找对应管道
   * \return true或false
   */
  auto record_inside_to_outside_call_info(GlobalIndex global_index,
                                          TransportPtr &transport,
                                          rpc::CallID &virtual_call_id) -> bool;
  /**
   * 获取外部连接管道.
   *
   * \param transport 内部管道
   * \param header 协议头
   * \param [OUT] global_index 得到的外部连接索引
   * \return 外部连接管道
   */
  auto get_outside_transport(TransportPtr &transport,
                             const RpcMsgHeader &header,
                             GlobalIndex &global_index) -> TransportPtr &;
  /**
   * 获取一个新的虚拟调用ID.
   *
   * \return
   */
  rpc::CallID new_virtual_id();
  /**
   * 加载配置.
   *
   * \return true或false
   */
  auto load_config() -> bool;
  /**
   * 内部服务推送/调用外部服务.
   *
   * \param transport 内部服务管道
   * \param header 协议头
   * \return true或false
   */
  auto inside_call_outside(TransportPtr &transport, const RpcMsgHeader &header)
      -> bool;
  /**
   * 内部服务调用返回到外部服务.
   *
   * \param transport 内部服务管道
   * \param header 协议头
   * \return true或false
   */
  auto inside_return_outside(TransportPtr &transport,
                             const RpcMsgHeader &header) -> bool;
  /**
   * 外部服务调用内部服务.
   *
   * \param transport 外部服务管道
   * \param header 协议头
   * \return true或false
   */
  auto outside_call_inside(TransportPtr &transport, const RpcMsgHeader &header)
      -> bool;
  /**
   * 外部服务调用返回到内部服务.
   *
   * \param transport 外部服务管道
   * \param header 协议头
   * \return true或false
   */
  auto outside_return_inside(TransportPtr &transport,
                             const RpcMsgHeader &header) -> bool;
  /**
   * 回收外部连接索引.
   *
   * \param global_index 外部连接索引
   * \return true或false
   */
  auto recycle_global_index(GlobalIndex global_index) -> bool;
  /**
   * 新增外部管道.
   *
   * \param global_index 外部连接索引
   * \param transport 外部管道
   * \return true或false
   */
  auto add_outside_transport(GlobalIndex global_index, TransportPtr &transport)
      -> bool;
  /**
   * 移除外部管道.
   *
   * \param transport 外部管道
   * \return true或false
   */
  auto remove_outside_transport(rpc::TransportPtr &transport) -> bool;
};

} // namespace rpc
